﻿/*******************************************************************************
 * Copyright (c) 2010-2011 Robert Fraser                                       *
 *                                                                             *
 * Permission is hereby granted, free of charge, to any person obtaining a     *
 * copy of this software and associated documentation files (the "Software"),  *
 * to deal in the Software without restriction, including without limitation   *
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,    *
 * and/or sell copies of the Software, and to permit persons to whom the       *
 * Software is furnished to do so, subject to the following conditions:        *
 *                                                                             *
 *   - The above copyright notice and this permission notice shall be included *
 *     in all copies or substantial portions of the Software.                  *
 *                                                                             *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  *
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,    *
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER      *
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING     *
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER         *
 * DEALINGS IN THE SOFTWARE.                                                   *
 *******************************************************************************/

using System.Diagnostics;
using System.IO;
using System.Windows.Input;
using AvalonDock;
using Microsoft.Win32;
using zeal.ui.util;

namespace zeal.ui
{
    internal abstract partial class DocumentEditor : DocumentContent
    {
        public const string FILE_TYPE_FILTER = "Graph Files (*.zgx)|*.zgx";

        private readonly UndoStack _undoStack;
        private int _undoIndex;
        private int _savedUndoIndex = -1;

        protected DocumentEditor()
        {
            _undoStack = new UndoStack();
            HasUnsavedChanges = true;
            this.bindCommand(ApplicationCommands.Undo, _undoStack.canUndo, doUndo);
            this.bindCommand(ApplicationCommands.Redo, _undoStack.canRedo, doRedo);
            this.bindCommand(ApplicationCommands.SaveAs, () => doSave(true));
            this.bindCommand(ApplicationCommands.Save, canSave, () => doSave(false));
            this.bindCommand(ApplicationCommands.Close, () => Close());
            Bindings.set(this, TitleProperty.of<string>(), Bindings.multi(
                Bindings.get(this, FilenameProperty),
                Bindings.get(this, HasUnsavedChangesProperty),
                (name, changed) => name == null ? "{ERROR: Filename is null}" : (Path.GetFileNameWithoutExtension(name) + (changed ? "*" : ""))));
        }

        protected void pushUndo(IMemento memento)
        {
            CheckAccess();
            if(memento == null)
                return;

            // If we've saved "after" our current position, then no combonation of undo/redo can
            // return to that position
            if(_savedUndoIndex > _undoIndex) 
                _savedUndoIndex = -1;

            _undoStack.push(memento);
            _undoIndex++;
            HasUnsavedChanges = true;
        }

        private void doUndo() { if(_undoStack.undo()) { _undoIndex--; updateHasChanges(); } }
        private void doRedo() { if(_undoStack.redo()) { _undoIndex++; updateHasChanges(); } }
        private void updateHasChanges() { Debug.Assert(_undoIndex >= 0); HasUnsavedChanges = _undoIndex != _savedUndoIndex; }

        private bool canSave() { return _undoIndex != _savedUndoIndex; }
        protected abstract bool saveDocument(Stream stream);
        private void doSave(bool saveAs)
        {
            if (!saveAs && !HasUnsavedChanges)
                return;

            // Show prompt if we have to
            string filename = Filename;
            if (filename == null || !Path.IsPathRooted(filename) || saveAs)
            {
                SaveFileDialog dlg = new SaveFileDialog { AddExtension = true, Filter = FILE_TYPE_FILTER, FileName = filename };
                filename = dlg.ShowDialog().GetValueOrDefault() ? dlg.FileName : null;
                if(filename == null)
                    return;
                Filename = filename;
            }

            bool success;
            using(Stream stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None))
                success = saveDocument(stream);
            
            if(success)
            {
                _savedUndoIndex = _undoIndex;
                updateHasChanges();
                HasUnsavedChanges = false;
            }
        }
    }
}